from xml.dom import minidom
from . import SMKernel

apply_edisp = False

class SpatialType(object):

    _UNDEFINED_TYPE = 0
    _POINTSOURCE_TYPE = 1
    _MAPCUBE_TYPE = 2
    _SPATIALMAP_TYPE = 3
    _ISOTROPIC_TYPE = 4
    _DISK_TYPE = 5
    _GAUSSIAN_TYPE = 6

    def __init__(self):
        self.sptype = self._UNDEFINED_TYPE
        self.ra, self.dec = None, None
        self.fitsfile = None
        self.radius = None

    def __repr__(self):
        if self.sptype == self._POINTSOURCE_TYPE:
            return 'PointSource: ra={}, dec={}'.format(self.ra, self.dec)
        elif self.sptype == self._MAPCUBE_TYPE:
            return 'Mapcube: file={}'.format(self.fitsfile)
        elif self.sptype == self._SPATIALMAP_TYPE:
            return 'SpatialMap: file={}'.format(self.fitsfile)
        elif self.sptype == self._ISOTROPIC_TYPE:
            return 'Isotropic'
        elif self.sptype == self._DISK_TYPE:
            return 'RadialDisk: ra={}, dec={}, radius={}'.format(self.ra, self.dec, self.radius)
        elif self.sptype == self._GAUSSIAN_TYPE:
            return 'RadialGaussian: ra={}, dec={}, sigma={}'.format(self.ra, self.dec, self.radius)
        return 'Undefined'

    @classmethod
    def PointSource(cls, ra, dec):
        newcls = cls()
        newcls.sptype = cls._POINTSOURCE_TYPE
        newcls.ra = float(ra)
        newcls.dec = float(dec)
        return newcls

    @classmethod
    def MapCube(cls, infile):
        newcls = cls()
        newcls.sptype = cls._MAPCUBE_TYPE
        newcls.fitsfile = infile
        return newcls

    @classmethod
    def SpatialMap(cls, infile):
        newcls = cls()
        newcls.sptype = cls._SPATIALMAP_TYPE
        newcls.fitsfile = infile
        return newcls

    @classmethod
    def Isotropic(cls):
        newcls = cls()
        newcls.sptype = cls._ISOTROPIC_TYPE
        return newcls

    @classmethod
    def RadialDisk(cls, ra, dec, radius):
        newcls = cls()
        newcls.sptype = cls._DISK_TYPE
        newcls.ra = float(ra)
        newcls.dec = float(dec)
        newcls.radius = float(radius)
        assert newcls.radius>0.
        return newcls

    @classmethod
    def RadialGaussian(cls, ra, dec, sigma):
        newcls = cls()
        newcls.sptype = cls._GAUSSIAN_TYPE
        newcls.ra = float(ra)
        newcls.dec = float(dec)
        newcls.radius = float(sigma)
        assert newcls.radius > 0.
        return newcls

    def get_backbone(self, srcname):
        if self.sptype == self._UNDEFINED_TYPE:
            raise RuntimeError('please define the spatial model!')

        xml_list = []
        if self.sptype == self._POINTSOURCE_TYPE:
            xml_list.append('<source name="%s" type="PointSource">' % srcname)
            xml_list.append('\t<!-- point source units are cm^-2 s^-1 MeV^-1 -->')
            xml_list.append('\t<spatialModel type="SkyDirFunction">')
            xml_list.append('\t\t<parameter free="0" max="360." min="-360." name="RA" scale="1.0" value="%s"/>'%(self.ra%360.))
            xml_list.append('\t\t<parameter free="0" max="90." min="-90." name="DEC" scale="1.0" value="%s"/>'%self.dec)
        else:
            xml_list.append('<source name="%s" type="DiffuseSource">' % srcname)
            xml_list.append('\t<!-- diffuse source units are cm^-2 s^-1 MeV^-1 sr^-1 -->')

            if self.sptype == self._MAPCUBE_TYPE:
                xml_list.append('\t<spatialModel file="%s" type="MapCubeFunction" map_based_integral="true">' % self.fitsfile)
                xml_list.append('\t\t<parameter free="0" max="1000.0" min="0.001" name="Normalization" scale="1.0" value="1.0"/>')

            elif self.sptype == self._SPATIALMAP_TYPE:
                xml_list.append('\t<spatialModel file="%s" type="SpatialMap" map_based_integral="true">' % self.fitsfile)
                xml_list.append('\t\t<parameter free="0" max="1000.0" min="0.001" name="Prefactor" scale="1.0" value="1.0"/>')

            elif self.sptype == self._ISOTROPIC_TYPE:
                xml_list.append('\t<spatialModel type="ConstantValue">')
                xml_list.append('\t\t<parameter free="0" max="10.0" min="0.0" name="Value" scale="1.0" value="1.0"/>')

            elif self.sptype == self._DISK_TYPE:
                xml_list.append('\t<spatialModel type="RadialDisk">')
                xml_list.append('\t\t<parameter free="0" max="360." min="-360." name="RA" scale="1.0" value="%s"/>'%(self.ra%360.))
                xml_list.append('\t\t<parameter free="0" max="90." min="-90." name="DEC" scale="1.0" value="%s"/>'%self.dec)
                xml_list.append('\t\t<parameter free="0" max="90" min="0" name="Radius" scale="1" value="%s" />'%self.radius)
                
            elif self.sptype == self._GAUSSIAN_TYPE:
                xml_list.append('\t<spatialModel type="RadialGaussian">')
                xml_list.append('\t\t<parameter free="0" max="360." min="-360." name="RA" scale="1.0" value="%s"/>'%(self.ra%360.))
                xml_list.append('\t\t<parameter free="0" max="90." min="-90." name="DEC" scale="1.0" value="%s"/>'%self.dec)
                xml_list.append('\t\t<parameter free="0" max="90" min="0" name="Sigma" scale="1" value="%s" />'%self.radius)
                
        xml_list.insert(2, '\t</spectrum>')
        xml_list.append('\t</spatialModel>')
        xml_list.append('</source>')
        return xml_list


def PowerLaw(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="PowerLaw">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="PowerLaw">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="PowerLaw">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000.0" min="0.0001" name="Prefactor" scale="1e-12" value="1."/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="1.0" name="Index" scale="-1.0" value="2.4"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1000000.0" min="100.0" name="Scale" scale="1.0" value="1000.0"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def PowerLaw2(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="PowerLaw2">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="PowerLaw2">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="PowerLaw2">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000.0" min="1e-05" name="Integral" scale="1e-08" value="1.0"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="1.0" name="Index" scale="-1.0" value="2.4"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="200000.0" min="20.0" name="LowerLimit" scale="1.0" value="200.0"/>')
    xml_list.insert(6, '\t\t<parameter free="0" max="200000.0" min="20.0" name="UpperLimit" scale="1.0" value="2e5"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def BrokenPowerLaw(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="BrokenPowerLaw">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="BrokenPowerLaw">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="BrokenPowerLaw">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000.0" min="0.001" name="Prefactor" scale="1e-09" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="1.0" name="Index1" scale="-1.0" value="1.8"/>')
    xml_list.insert(5, '\t\t<parameter free="1" max="1000000.0" min="100.0" name="BreakValue" scale="1.0" value="1000.0"/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="5.0" min="1.0" name="Index2" scale="-1.0" value="2.3"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def SmoothBrokenPowerLaw(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="SmoothBrokenPowerLaw">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="SmoothBrokenPowerLaw">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="SmoothBrokenPowerLaw">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1e5" min="1e-5" name="Prefactor" scale="1e-09" value="1.0"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="1.0" name="Index1" scale="-1.0" value="1.4"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1000000.0" min="100.0" name="Scale" scale="1.0" value="200.0"/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="5.0" min="1.0" name="Index2" scale="-1.0" value="2.4"/>')
    xml_list.insert(7, '\t\t<parameter free="1" max="5e5" min="20" name="BreakValue" scale="1.0" value="400"/>')
    xml_list.insert(8, '\t\t<parameter free="0" max="10" min="0.01" name="Beta" scale="1.0" value="0.2"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def LogParabola(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="LogParabola">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="LogParabola">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="LogParabola">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000.0" min="0.001" name="norm" scale="1e-09" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="10" min="0" name="alpha" scale="1.0" value="2.4"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1e4" min="20" name="Eb" scale="1" value="300."/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="10" min="-10" name="beta" scale="1.0" value="0."/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def ConstantValue(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="ConstantValue">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="ConstantValue">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="ConstantValue">')
    xml_list.insert(3, '\t\t<parameter free="0" max="10" min="0" name="Value" scale="1" value="1"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def FileFunction(name, spatobj, spectrum_file, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" file="%s" type="FileFunction">' % spectrum_file)
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum file="%s" type="FileFunction">' % spectrum_file)
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" file="%s" type="FileFunction">' % spectrum_file)
    xml_list.insert(3, '\t\t<parameter free="1" max="1e4" min="1e-4" name="Normalization" scale="1" value="1.0"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def PLSuperExpCutoff(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="PLSuperExpCutoff">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="PLSuperExpCutoff">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="PLSuperExpCutoff">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000" min="1e-05" name="Prefactor" scale="1e-07" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="0.0" name="Index1" scale="-1" value="1.7"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1000000.0" min="100.0" name="Scale" scale="1" value="200"/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="300000" min="500" name="Cutoff" scale="1" value="3000"/>')
    xml_list.insert(7, '\t\t<parameter free="0" max="5" min="0" name="Index2" scale="1" value="1"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def PLSuperExpCutoff2(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="PLSuperExpCutoff2">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="PLSuperExpCutoff2">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="PLSuperExpCutoff2">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000" min="1e-05" name="Prefactor" scale="1e-07" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="0.0" name="Index1" scale="-1" value="1.7"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1000000.0" min="100.0" name="Scale" scale="1" value="200"/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="1" min="-1" name="Expfactor" scale="1.0" value="0.001"/>')
    xml_list.insert(7, '\t\t<parameter free="0" max="5" min="0" name="Index2" scale="1" value="1"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def PLSuperExpCutoff3(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="PLSuperExpCutoff3">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="PLSuperExpCutoff3">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="PLSuperExpCutoff3">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000" min="1e-05" name="Prefactor" scale="1e-07" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="0.0" name="Index1" scale="-1" value="1.7"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1000000.0" min="100.0" name="Scale" scale="1" value="200"/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="10" min="0" name="Expfactor2" scale="1.0" value="2"/>')
    xml_list.insert(7, '\t\t<parameter free="0" max="5" min="0" name="Index2" scale="1" value="1"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def PLSuperExpCutoff4(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="PLSuperExpCutoff4">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="PLSuperExpCutoff4">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="PLSuperExpCutoff4">')
    xml_list.insert(3, '\t\t<parameter free="1" max="1000" min="1e-05" name="Prefactor" scale="1e-11" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="1" max="5.0" min="0.0" name="IndexS" scale="-1" value="1.7"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1000000.0" min="100.0" name="Scale" scale="1" value="1000"/>')
    xml_list.insert(6, '\t\t<parameter free="1" max="5" min="0" name="ExpfactorS" scale="1.0" value="0.4"/>')
    xml_list.insert(7, '\t\t<parameter free="0" max="2" min="-0.5" name="Index2" scale="1" value="1"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)

def Gaussian(name, spatobj, edispon=None):
    xml_list = spatobj.get_backbone(name)
    if edispon or (edispon is None and apply_edisp):
        xml_list.insert(1, '\t<spectrum apply_edisp="true" type="Gaussian">')
    elif apply_edisp is None:
        xml_list.insert(1, '\t<spectrum type="Gaussian">')
    else:
        xml_list.insert(1, '\t<spectrum apply_edisp="false" type="Gaussian">')

    xml_list.insert(3, '\t\t<parameter free="1" max="1000.0" min="0.001" name="Prefactor" scale="1e-09" value="1"/>')
    xml_list.insert(4, '\t\t<parameter free="0" max="1e5" min="1e3" name="Mean" scale="1.0" value="7e4"/>')
    xml_list.insert(5, '\t\t<parameter free="0" max="1e5" min="30" name="Sigma" scale="1.0" value="1e3"/>')
    xml_string = '\n'.join(xml_list)

    dom = minidom.parseString(xml_string)
    node = dom.childNodes[0]
    return SMKernel.Source(node)
